package com.chatplus.application.controller.api;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.chatplus.application.common.constant.RedisPrefix;
import com.chatplus.application.common.enumeration.AccountErrorCode;
import com.chatplus.application.common.enumeration.UserStatusEnum;
import com.chatplus.application.common.exception.BadRequestException;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.common.util.RedisUtils;
import com.chatplus.application.controller.admin.account.AccountAdminApiController;
import com.chatplus.application.domain.dto.AdminConfigDto;
import com.chatplus.application.domain.dto.RewardDto;
import com.chatplus.application.domain.dto.UserCustomChatConfigDto;
import com.chatplus.application.domain.entity.account.InviteCodeEntity;
import com.chatplus.application.domain.entity.account.InviteLogEntity;
import com.chatplus.application.domain.entity.account.UserEntity;
import com.chatplus.application.domain.entity.chat.ChatHistoryEntity;
import com.chatplus.application.domain.request.AccountLoginRequest;
import com.chatplus.application.domain.request.AccountRegisterRequest;
import com.chatplus.application.domain.request.AccountResetPassRequest;
import com.chatplus.application.domain.request.AccountUpdatePassRequest;
import com.chatplus.application.domain.request.AccountUpdateRequest;
import com.chatplus.application.domain.response.AccountProfileResponse;
import com.chatplus.application.domain.response.AccountSessionResponse;
import com.chatplus.application.domain.response.WechatLoginQrResponse;
import com.chatplus.application.service.account.InviteCodeService;
import com.chatplus.application.service.account.InviteLogService;
import com.chatplus.application.service.account.UserProductLogService;
import com.chatplus.application.service.account.UserService;
import com.chatplus.application.service.account.WechatUserService;
import com.chatplus.application.service.auth.authentication.UserAuthenticationService;
import com.chatplus.application.service.auth.request.AccountAuthenticationRequest;
import com.chatplus.application.service.basedata.ConfigService;
import com.chatplus.application.service.chat.ChatHistoryService;
import com.chatplus.application.service.verification.PlusCaptchaService;
import com.chatplus.application.web.basecontroller.BaseController;
import com.chatplus.application.web.util.IpUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.codec.SerializationCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.hutool.core.lang.Assert.isTrue;

/**
 * 用户相关API
 */
@Validated
@RestController
@RequestMapping("/api/user")
@Tag(name = "用户信息API", description = "用户信息API")
public class AccountApiController extends BaseController {
    static final SouthernQuietLogger LOGGER = SouthernQuietLoggerFactory.getLogger(AccountApiController.class);
    private final UserAuthenticationService userAuthenticationService;
    private final UserService usersService;
    private final ConfigService configService;
    private final InviteCodeService inviteCodeService;
    private final RedissonClient redissonClient;
    private final UserProductLogService userProductLogService;
    private final ChatHistoryService chatHistoryService;
    private final InviteLogService inviteLogService;
    private final WechatUserService wechatUserService;


    @Autowired
    public AccountApiController(UserAuthenticationService userAuthenticationService,
                                UserService usersService,
                                ConfigService configService,
                                InviteCodeService inviteCodeService,
                                RedissonClient redissonClient,
                                UserProductLogService userProductLogService,
                                ChatHistoryService chatHistoryService,
                                InviteLogService inviteLogService,
                                WechatUserService wechatUserService
    ) {
        this.userAuthenticationService = userAuthenticationService;
        this.usersService = usersService;
        this.configService = configService;
        this.inviteCodeService = inviteCodeService;
        this.redissonClient = redissonClient;
        this.userProductLogService = userProductLogService;
        this.chatHistoryService = chatHistoryService;
        this.inviteLogService = inviteLogService;
        this.wechatUserService = wechatUserService;
    }

    /**
     * 生成绑定微信的二维码
     *
     * @return 二维码对应的系统ID(用于触发扫码后的回调)
     */
    @GetMapping("generateBindQrCode")
    @Operation(summary = "生成绑定微信的二维码")
    public WechatLoginQrResponse generateBindQrCode() {
        return wechatUserService.generateBindQrCode(getUserId());
    }

    /**
     * 获取登录的二维码
     *
     * @return 二维码对应的系统ID(用于触发扫码后的回调)
     */
    @GetMapping("getLoginQrCode")
    @SaIgnore
    @Operation(summary = "获取登录的二维码")
    public WechatLoginQrResponse getLoginQrCode() {
        return wechatUserService.getLoginQrCode();
    }

    @GetMapping("wechatOpenIdLogin")
    @SaIgnore
    @Operation(summary = "查询用户是否扫码成功")
    public Map<String, String> wechatOpenIdLogin(@RequestParam String ticket, HttpServletRequest request) {
        // 这里的sceneStr是上面接口返回的，这步操作是微信事件推送之后会存一个redis
        RBucket<Long> bucket = redissonClient.getBucket(ticket);
        Long userId = bucket.isExists() ? bucket.get() : null;
        if (userId != null && userId != -1) {
            UserEntity userEntity = usersService.getById(userId);
            if (userEntity == null) {
                throw new BadRequestException("用户不存在-请联系管理员处理：" + userId);
            }
            AccountAuthenticationRequest accountAuthenticationRequest = new AccountAuthenticationRequest();
            accountAuthenticationRequest.setUsername(userEntity.getUsername());
            accountAuthenticationRequest.setPassword(userEntity.getPassword());
            accountAuthenticationRequest.setRemoteIp(IpUtil.getRemoteIp(request));
            bucket.delete();
            return Map.of("token", userAuthenticationService.authenticate(accountAuthenticationRequest).getAuthorizationToken());
        }
        if (userId == null) {
            throw new BadRequestException("登录已过期，请重新扫码");
        }
        return Collections.emptyMap();
    }

    /**
     * 登录
     */
    @PostMapping("/login")
    @Operation(summary = "用户登录")
    @SaIgnore
    public Map<String, String> login(@RequestBody @Valid AccountLoginRequest accountLoginRequest, HttpServletRequest request) {
        LOGGER.message("用户发起登录请求").context("AccountRegisterRequest", accountLoginRequest).info();
        verifyCode(accountLoginRequest.getUuid(), accountLoginRequest.getCode());
        return Map.of("token", AccountAdminApiController.getTokenString(accountLoginRequest, request, usersService, userAuthenticationService));
    }

    public void verifyCode(String uuid, String userInputCode) {
        if (StringUtils.isEmpty(userInputCode)) {
            throw new BadRequestException("验证码不能为空");
        }
        String verifyKey = RedisPrefix.USER_LOGIN_PREFIX + uuid;
        RBucket<String> rBucket = RedisUtils.getClient().getBucket(verifyKey);
        String code = rBucket.isExists() ? rBucket.get() : null;
        if (code == null) {
            throw new BadRequestException("验证码已过期，请重新获取");
        }
        if (!code.equalsIgnoreCase(userInputCode)) {
            throw new BadRequestException("验证码错误");
        }
    }

    @PostMapping("/register")
    @SaIgnore
    @Operation(summary = "用户注册")
    public String register(@RequestBody @Valid AccountRegisterRequest accountRegisterRequest, HttpServletRequest request) {
        LOGGER.message("用户发起注册请求").context("AccountRegisterRequest", accountRegisterRequest).info();
        // 保存用户
        AdminConfigDto adminConfigDto = configService.getSystemConfig();
        if (adminConfigDto == null) {
            throw new BadRequestException("当前系统禁止注册");
        }
        String username = accountRegisterRequest.getUsername();
        List<String> registerWays = adminConfigDto.getRegisterWays();
        if (CollectionUtils.isEmpty(registerWays)) {
            throw new BadRequestException("当前系统禁止注册");
        }
        // 判断username 是邮箱还是手机号
        if (!registerWays.contains("mobile") && PhoneUtil.isMobile(username)) {
            throw new BadRequestException("当前系统禁止手机号注册");
        }
        if (!registerWays.contains("email") && Validator.isEmail(username)) {
            throw new BadRequestException("当前系统禁止邮箱注册");
        }
        isTrue(accountRegisterRequest.getPassword().length() >= 8 && accountRegisterRequest.getPassword().length() <= 16, AccountErrorCode.NEW_PASSWORD_INVALID);
        isTrue(Objects.equals(accountRegisterRequest.getPassword(), accountRegisterRequest.getRepass()), AccountErrorCode.NEW_PASSWORD_AND_CONFIRM_PASSWORD_NOT_EQUAL);
        isTrue(usersService.getByUsername(username) == null, AccountErrorCode.PHONE_NUMBER_USED_BY_ANOTHER);
        PlusCaptchaService plusCaptchaService = PlusCaptchaService.getInstance(username);
        boolean check = plusCaptchaService.checkCaptcha(username, accountRegisterRequest.getCode());
        // 校验入参
        isTrue(check, AccountErrorCode.CAPTCHA_IS_INVALID);

        UserEntity accountEntity = BeanUtil.copyProperties(accountRegisterRequest, UserEntity.class, "password");
        accountEntity.setNickname("用户@" + RandomUtil.randomString(5));
        accountEntity.setPassword(BCrypt.hashpw(accountRegisterRequest.getPassword()));
        // 设置其他必填信息,初始化用户的其他信息
        accountEntity.setAvatar("/images/avatar/user.png");
        accountEntity.setStatus(UserStatusEnum.OK);
        accountEntity.setChatRoles(List.of("gpt"));
        accountEntity.setChatConfig(new UserCustomChatConfigDto());
        accountEntity.setChatModels(adminConfigDto.getDefaultModels());
        usersService.save(accountEntity);
        // 注册成功后给与用户一定的免费次数
        userProductLogService.adminAddPowerToUser(accountEntity.getId(), adminConfigDto.getInitPower(), "注册赠送");
        // 对邀请的用户进行奖励
        String inviteCode = accountRegisterRequest.getInviteCode();
        handleInvite(inviteCode, adminConfigDto, accountEntity);
        // 登录操作
        AccountAuthenticationRequest accountAuthenticationRequest = new AccountAuthenticationRequest();
        accountAuthenticationRequest.setUsername(accountRegisterRequest.getUsername());
        accountAuthenticationRequest.setPassword(accountRegisterRequest.getPassword());
        accountAuthenticationRequest.setRemoteIp(IpUtil.getRemoteIp(request));
        return userAuthenticationService.authenticate(accountAuthenticationRequest).getAuthorizationToken();
    }

    /**
     * 处理邀请
     */
    private void handleInvite(String inviteCode, AdminConfigDto adminConfigDto, UserEntity accountEntity) {
        if (StringUtils.isNotEmpty(inviteCode)) {
            // TODO 后续这步发通知异步处理，不影响注册流程
            InviteCodeEntity inviteCodeEntity = inviteCodeService.getByCode(inviteCode);
            if (inviteCodeEntity != null) {
                // 更新邀请统计
                inviteCodeService.update(new LambdaUpdateWrapper<InviteCodeEntity>()
                        .eq(InviteCodeEntity::getCode, inviteCode)
                        .setSql("reg_num = reg_num + 1"));
                // 邀请码存在，给与邀请人奖励
                userProductLogService.adminAddPowerToUser(inviteCodeEntity.getUserId(), adminConfigDto.getInvitePower(), "邀请【" + accountEntity.getUsername() + "】用户注册奖励");
                // 保存邀请记录
                InviteLogEntity inviteLogEntity = new InviteLogEntity();
                inviteLogEntity.setInviterId(inviteCodeEntity.getUserId());
                inviteLogEntity.setUserId(accountEntity.getId());
                inviteLogEntity.setUsername(accountEntity.getUsername());
                inviteLogEntity.setInviteCode(inviteCode);
                RewardDto rewardDto = new RewardDto();
                rewardDto.setPower(adminConfigDto.getInvitePower());
                inviteLogEntity.setReward(rewardDto);
                inviteLogService.save(inviteLogEntity);
            }
        }
    }

    @GetMapping("/session")
    @Operation(summary = "获取用户会话")
    public AccountSessionResponse session() {
        String redisKey = RedisPrefix.SESSION_PREFIX + getUserId();
        RBucket<AccountSessionResponse> bucket = redissonClient.getBucket(redisKey, new SerializationCodec());
        AccountSessionResponse accountSessionResponse = bucket.isExists() ? bucket.get() : null;
        if (accountSessionResponse != null) {
            return accountSessionResponse;
        }
        UserEntity userEntity = usersService.getById(getUserId());
        if (userEntity == null) {
            StpUtil.logout();
            throw new BadRequestException(AccountErrorCode.ACCOUNT_NOT_EXIST.getMsg());
        }
        if (userEntity.getStatus() != UserStatusEnum.OK) {
            StpUtil.logout();
            throw new BadRequestException(AccountErrorCode.ACCOUNT_HAS_LOCKED.getMsg());
        }
        accountSessionResponse = buildAccountSession(userEntity);

        // 设置30s的缓存
        bucket.set(accountSessionResponse, Duration.ofSeconds(30));
        return accountSessionResponse;
    }

    @GetMapping("/profile")
    @Operation(summary = "获取用户信息")
    public AccountProfileResponse profile() {
        UserEntity userEntity = usersService.getById(getUserId());
        AccountProfileResponse accountProfileResponse = BeanUtil.copyProperties(userEntity, AccountProfileResponse.class);
        accountProfileResponse.setPower(userProductLogService.getUserChatPower(userEntity.getId()));
        // 以下信息为消费会话历史得到的
        List<ChatHistoryEntity> replyHistoryList = chatHistoryService.getReplyHistoryList(userEntity.getId());
        // 总的token
        accountProfileResponse.setTotalToken(chatHistoryService.getTotalToken(replyHistoryList));
        // 获取本月消费的token
        accountProfileResponse.setToken(chatHistoryService.getThisMonthToken(replyHistoryList));
        accountProfileResponse.setVip(userEntity.getVipExpiredTime() != null
                && userEntity.getVipExpiredTime().isAfter(Instant.now()));
        accountProfileResponse.setExpiredTime(userEntity.getVipExpiredTime() != null
                ? userEntity.getVipExpiredTime().getEpochSecond()
                : null);
        return accountProfileResponse;
    }

    @PostMapping("/profile/update")
    @Operation(summary = "修改用户信息")
    public void updateProfile(@RequestBody AccountUpdateRequest accountUpdateRequest) {
        UserEntity accountEntity = usersService.getById(getUserId());
        isTrue(accountEntity != null, AccountErrorCode.ACCOUNT_NOT_EXIST);
        assert accountEntity != null;
        accountEntity.setNickname(accountUpdateRequest.getNickname());
        if (StringUtils.isNotEmpty(accountUpdateRequest.getAvatar())) {
            accountEntity.setAvatar(accountUpdateRequest.getAvatar());
        }
        usersService.updateById(accountEntity);
    }

    @PostMapping("/bind/username")
    @Operation(summary = "修改绑定账户")
    public void bindUsername(@RequestBody AccountUpdateRequest accountUpdateRequest) {
        PlusCaptchaService plusCaptchaService = PlusCaptchaService.getInstance(accountUpdateRequest.getUsername());
        boolean check = plusCaptchaService.checkCaptcha(accountUpdateRequest.getUsername(), accountUpdateRequest.getCode());
        // 校验入参
        isTrue(check, AccountErrorCode.CAPTCHA_IS_INVALID);
        UserEntity accountEntity = usersService.getById(getUserId());
        isTrue(accountEntity != null, AccountErrorCode.ACCOUNT_NOT_EXIST);
        assert accountEntity != null;
        accountEntity.setUsername(accountUpdateRequest.getUsername());
        usersService.updateById(accountEntity);
    }

    @PostMapping("/resetPass")
    @SaIgnore
    @Operation(summary = "重置密码")
    public void resetPass(@RequestBody AccountResetPassRequest resetPassRequest) {
        isTrue(resetPassRequest.getPassword().length() >= 8 && resetPassRequest.getPassword().length() <= 16, AccountErrorCode.NEW_PASSWORD_INVALID);
        isTrue(Objects.equals(resetPassRequest.getPassword(), resetPassRequest.getRepass()), AccountErrorCode.NEW_PASSWORD_AND_CONFIRM_PASSWORD_NOT_EQUAL);
        PlusCaptchaService plusCaptchaService = PlusCaptchaService.getInstance(resetPassRequest.getUsername());
        boolean check = plusCaptchaService.checkCaptcha(resetPassRequest.getUsername(), resetPassRequest.getCode());


        // 校验入参
        isTrue(check, AccountErrorCode.CAPTCHA_IS_INVALID);
        UserEntity accountEntity = usersService.getByUsername(resetPassRequest.getUsername());
        isTrue(accountEntity != null, AccountErrorCode.ACCOUNT_NOT_EXIST);

        LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserEntity::getId, accountEntity.getId());
        updateWrapper.set(UserEntity::getPassword, BCrypt.hashpw(resetPassRequest.getPassword()));
        usersService.update(updateWrapper);
    }

    @PostMapping("/password")
    @Operation(summary = "修改密码")
    public void password(@RequestBody AccountUpdatePassRequest resetPassRequest) {
        UserEntity accountEntity = usersService.getById(getUserId());
        isTrue(accountEntity != null, AccountErrorCode.ACCOUNT_NOT_EXIST);
        assert accountEntity != null;
        isTrue(BCrypt.checkpw(resetPassRequest.getOldPass(), accountEntity.getPassword()), AccountErrorCode.OLD_PASSWORD_INVALID);
        isTrue(resetPassRequest.getPassword().length() >= 8 && resetPassRequest.getPassword().length() <= 16, AccountErrorCode.NEW_PASSWORD_INVALID);
        isTrue(resetPassRequest.getPassword().equals(resetPassRequest.getRepass()), AccountErrorCode.NEW_PASSWORD_AND_CONFIRM_PASSWORD_NOT_EQUAL);
        LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserEntity::getId, getUserId());
        updateWrapper.set(UserEntity::getPassword, BCrypt.hashpw(resetPassRequest.getPassword()));
        usersService.update(updateWrapper);
    }

    /**
     * 退出当前登录的账号
     */
    @GetMapping("/logout")
    @Operation(summary = "用户退出登录")
    public void logout() {
        StpUtil.logout();
    }

    public AccountSessionResponse buildAccountSession(UserEntity userEntity) {
        AccountSessionResponse accountSessionResponse = new AccountSessionResponse();
        accountSessionResponse.setAvatar(userEntity.getAvatar());
        accountSessionResponse.setNickname(userEntity.getNickname());
        accountSessionResponse.setUsername(userEntity.getUsername());
        accountSessionResponse.setChatConfig(userEntity.getChatConfig());
        accountSessionResponse.setChatRoles(userEntity.getChatRoles());
        accountSessionResponse.setChatModels(userEntity.getChatModels());
        accountSessionResponse.setUpdatedAt(userEntity.getUpdatedAt().getEpochSecond());
        accountSessionResponse.setCreatedAt(userEntity.getCreatedAt().getEpochSecond());
        accountSessionResponse.setStatus(userEntity.getStatus() == UserStatusEnum.OK);
        accountSessionResponse.setLastLoginAt(userEntity.getLastLoginAt() != null ? userEntity.getLastLoginAt().getEpochSecond() : null);
        accountSessionResponse.setLastLoginIp(userEntity.getLastLoginIp());
        accountSessionResponse.setId(userEntity.getId());
        accountSessionResponse.setAdmin(userEntity.getAdmin() != null && userEntity.getAdmin());
        // 以下信息为查询订单信息得到的
        accountSessionResponse.setVip(userEntity.getVipExpiredTime() != null
                && userEntity.getVipExpiredTime().isAfter(Instant.now()));
        accountSessionResponse.setExpiredTime(userEntity.getVipExpiredTime() != null
                ? userEntity.getVipExpiredTime().getEpochSecond()
                : null);
        // 从产品列表中获取用户的剩余次数
        accountSessionResponse.setPower(userProductLogService.getUserChatPower(userEntity.getId()));
        // 以下信息为消费会话历史得到的
        List<ChatHistoryEntity> replyHistoryList = chatHistoryService.getReplyHistoryList(userEntity.getId());
        // 总的token
        accountSessionResponse.setTotalToken(chatHistoryService.getTotalToken(replyHistoryList));
        // 获取本月消费的token
        accountSessionResponse.setToken(chatHistoryService.getThisMonthToken(replyHistoryList));
        return accountSessionResponse;
    }

}
